// File TerroristStates.cpp
//
// Purpose: Contains the states for the player
//
// Author : Bryson King (edited)
// Original Code Provided by: Mat Buckland 2002 (fup@ai-junkie.com)
// Date : 18 March 2013
// --------------------------------------------------------------
#include "TerroristStates.h"
#include "AbstractState.h"
#include "Terrorist.h"
#include "Telegram.h"
#include "MessageDispatcher.h"
#include "MessageTypes.h"
#include "Time/CrudeTimer.h"
#include "EntityNames.h"
#include "Random.h"

#include <iostream>
#include <stack>
using std::cout;
using std::endl;

//------------------------------------------------------------------------methods for Attack
Attack* Attack::Instance()
{
	static Attack instance;
	return &instance;
}

void Attack::Enter(CTerrorist* pTerrorist)
{
	#ifdef DEBUG_SECTION
		// Output status of the terrorist
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Entering the Attack state";
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Health is currently at " << 
			pTerrorist->Health();
	#endif
}

void Attack::Execute(CTerrorist* pTerrorist)
{
	//if terrorist's health drops less than 25%, run away
	if (pTerrorist->Health() < (.25 * MaxHealthLevel))
	{
		pTerrorist->GetFSM()->ChangeState(RunAway::Instance());
	}

	//if health not an issue, attack enemies in range
	if (pTerrorist->CurrentLocation()->NumberOfPeopleAtLocation() > 1)
	{
		// Attack any non-terrorist entities at location
	}

	//if no enemies in range, proceed toward the bridge
	if (pTerrorist->CurrentLocation()->NumberOfPeopleAtLocation() == 1)
		pTerrorist->GetFSM()->ChangeState(ProceedTowardBridge::Instance());
}

void Attack::Exit(CTerrorist* pTerrorist)
{
	#ifdef DEBUG_SECTION
		// Output status of the terrorist
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Exiting the Attack state";
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Health is currently at " << 
			pTerrorist->Health();
	#endif
}

bool Attack::OnMessage(CTerrorist* agent, const Telegram& msg)
{
	//send msg to global message handler
	return false;
}

//------------------------------------------------------------------------methods for RunAway

RunAway* RunAway::Instance()
{
	static RunAway instance;
	return &instance;
}

void RunAway::Enter(CTerrorist* pTerrorist)
{  
	#ifdef DEBUG_SECTION
		// Output status of the terrorist
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Entering the RunAway state";
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Health is currently at " << 
			pTerrorist->Health();
	#endif
}

void RunAway::Execute(CTerrorist* pTerrorist)
{
	//if no enemies around, stop running away and hide until healed
	/*
	if (true)
	{
		pTerrorist->GetFSM()->ChangeState(Hide::Instance());
	}
	*/
	//if health at 75% or more, proceed to bridge
	if (pTerrorist->Health() >= (.75 * MaxHealthLevel))
	{
		pTerrorist->GetFSM()->ChangeState(ProceedTowardBridge::Instance());
	}
}

void RunAway::Exit(CTerrorist* pTerrorist)
{
	#ifdef DEBUG_SECTION
		// Output status of the terrorist
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Exiting the RunAway state";
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Health is currently at " << 
			pTerrorist->Health();
	#endif
}

bool RunAway::OnMessage(CTerrorist* agent, const Telegram& msg)
{
	//send msg to global message handler
	return false;
}
//------------------------------------------------------------------------methods for ProceedTowardBridge

ProceedTowardBridge* ProceedTowardBridge::Instance()
{
	static ProceedTowardBridge instance;
	return &instance;
}

void ProceedTowardBridge::Enter(CTerrorist* pTerrorist)
{
	#ifdef DEBUG_SECTION
		// Output status of the terrorist
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Entering the ProceedTowardBridge state";
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Health is currently at " << 
			pTerrorist->Health();
	#endif
}

void ProceedTowardBridge::Execute(CTerrorist* pTerrorist)
{
	CLocation** aDirectionsAvailable;
	int iNumberOfPossibleDirections;
	int iDirectionChosen;
	int l_iNumberOfEnemiesHere;
	int l_iNumberOfUnvisitedDirections = 0;
	stack<CLocation*> l_LocationsAvailable;
	CLocation* l_oTempLocation = nullptr;
	IPerson** l_aEnemiesList = nullptr;

	// Check if there are other people at the location
	l_aEnemiesList = pTerrorist->CurrentLocation()->RetrieveAllEnemies(pTerrorist, l_iNumberOfEnemiesHere);
	if (l_iNumberOfEnemiesHere > 0)
	{
		pTerrorist->GetFSM()->ChangeState(Attack::Instance());

		//hide from enemy if weaker than enemy
		/*if (false)
		{
			pTerrorist->GetFSM()->ChangeState(Hide::Instance());
		}*/
	}	

	// Initialize array of direction options
	aDirectionsAvailable = pTerrorist->CurrentLocation()->RequestDirectionsPossible(iNumberOfPossibleDirections);

	// Add unvisited locations to the stack of possible locations
	for (int i = 0; i < iNumberOfPossibleDirections; i++)
	{
		// If not visited already, add to the stack
		if (!pTerrorist->GetLocationMachine()->LocationVisitedAlready(aDirectionsAvailable[i]))
		{
			l_LocationsAvailable.push(aDirectionsAvailable[i]);
			l_iNumberOfUnvisitedDirections++;
		}
	}

	// If there is an unvisited location, go there
	if (l_iNumberOfUnvisitedDirections > 0)
	{
		iDirectionChosen = CRandom::GetRandomNumber(l_iNumberOfUnvisitedDirections);

		// Remove appropriate number of items from the stack
		for (int i = 0; i < iDirectionChosen; i++)
		{
			l_LocationsAvailable.pop();
		}

		// Apply the top item from the stack to the location for the terrorist
		l_oTempLocation = l_LocationsAvailable.top();
		pTerrorist->SetNewLocation(l_oTempLocation);
		pTerrorist->GetLocationMachine()->ChangeLocation(l_oTempLocation);

	} else {// Otherwise, go to previous location to look for unvisited locations
		l_oTempLocation = pTerrorist->GetLocationMachine()->PreviousLocation();
		pTerrorist->SetNewLocation(l_oTempLocation);
		pTerrorist->GetLocationMachine()->ChangeLocation(l_oTempLocation);
	}
}

void ProceedTowardBridge::Exit(CTerrorist* pTerrorist)
{ 
	#ifdef DEBUG_SECTION
		// Output status of the terrorist
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Exiting the ProceedTowardBridge state";
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Health is currently at " << 
			pTerrorist->Health();
	#endif
}


bool ProceedTowardBridge::OnMessage(CTerrorist* agent, const Telegram& msg)
{
	//send msg to global message handler
	return false;
}

//------------------------------------------------------------------------methods for Hide

Hide* Hide::Instance()
{
	static Hide instance;
	return &instance;
}

void Hide::Enter(CTerrorist* pTerrorist)
{
	#ifdef DEBUG_SECTION
		// Output status of the terrorist
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Entering the Hide state";
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Health is currently at " << 
			pTerrorist->Health();
	#endif
}

void Hide::Execute(CTerrorist* pTerrorist)
{
	//if no enemies around, stop running away and wait until healed
	if (true)
	{
		pTerrorist->SetHealth(HealthHealedPerTick + pTerrorist->Health());
	}

	//if health returned to 75%, go back to previous proceeding toward the bridge
	if (pTerrorist->Health() >= (.75 * MaxHealthLevel))
	{
		pTerrorist->GetFSM()->ChangeState(ProceedTowardBridge::Instance());
	}
}


void Hide::Exit(CTerrorist* pTerrorist)
{ 
	#ifdef DEBUG_SECTION
		// Output status of the terrorist
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Exiting the Hide state";
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Health is currently at " << 
			pTerrorist->Health();
	#endif
}


bool Hide::OnMessage(CTerrorist* agent, const Telegram& msg)
{
	//send msg to global message handler
	return false;
}

//------------------------------------------------------------------------methods for Hijack

Hijack* Hijack::Instance()
{
	static Hijack instance;
	return &instance;
}


void Hijack::Enter(CTerrorist* pTerrorist)
{
	#ifdef DEBUG_SECTION
		// Output status of the terrorist
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Entering the Hijack state";
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Health is currently at " << 
			pTerrorist->Health();
	#endif
}

void Hijack::Execute(CTerrorist* pTerrorist)
{
	//if health < 25%, run away
	if (pTerrorist->Health() < (.25 * MaxHealthLevel))
	{
		pTerrorist->GetFSM()->ChangeState(RunAway::Instance());
	}

	//if bridge control successful
	if (true)
	{
		//win the game!!!
	}
}

void Hijack::Exit(CTerrorist* pTerrorist)
{ 
	#ifdef DEBUG_SECTION
		// Output status of the terrorist
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Exiting the Hijack state";
		cout << endl << GetNameOfEntity(pTerrorist->ID()) << ": " << "Health is currently at " << 
			pTerrorist->Health();
	#endif
}


bool Hijack::OnMessage(CTerrorist* agent, const Telegram& msg)
{
	//send msg to global message handler
	return false;
}


